# app.py
from flask import Flask, render_template, jsonify, request, send_file
from LX225BusServo import LX225BusServo as BusServo
import json
import time
import threading
import serial
import io
from datetime import datetime




app = Flask(__name__)
serial_lock = threading.Lock()
global servo_controller  # 全局变量声明
servo_controller = None  # 初始化时需要通过UART连接

# 临时存储动作序列
action_sequence = []
current_positions = {}
servo_modes = {}

@app.route('/')
def index():
    return render_template('control.html')

@app.route('/scan_servos')
def scan_servos():
    found = []
    try:
        for i in range(1,32):#1,254#加速:1,32
            sid = servo_controller.get_servo_id(i)
            if sid:
                found.append(sid)
                current_positions[sid] = servo_controller.read_angle(sid)
                servo_modes[sid] = 0  # 默认普通模式
            print(f"扫描{i}号舵机: {sid}", current_positions)
    except Exception as e:
        pass
        print(f"扫描{i}失败: {str(e)}")
    return jsonify({"servos": found})

@app.route('/set_position', methods=['POST'])
def set_position():
    data = request.json
    servo_id = data['id']
    position = data['position']
    print(data)
    try:
        servo_controller.set_angle(servo_id, position, 500)
        return jsonify(success=True, message=f"舵机{servo_id}位置已更新为{position}")
    except Exception as e:
        return jsonify(success=False, error=str(e)), 500

@app.route('/toggle_lock', methods=['POST'])
def toggle_lock():
    data = request.json
    servo_id = data['id']
    lock = data['lock']  # True表示锁定，False表示解锁
    
    try:
        if lock:
            # 锁定舵机并读取位置
            servo_controller.servo_on(servo_id)
            current_position = servo_controller.read_angle(servo_id)
            return jsonify(
                success=True, 
                message=f"舵机{servo_id}已锁定，当前位置: {current_position}"
            )
        else:
            # 解锁舵机
            servo_controller.servo_off(servo_id)
            return jsonify(
                success=True, 
                message=f"舵机{servo_id}已解锁"
            )
    except Exception as e:
        return jsonify(success=False, error=str(e)), 500

@app.route('/bulk_lock', methods=['POST'])
def bulk_lock():
    # 创建临时副本避免遍历时修改字典
    servo_ids = list(current_positions.keys())
    
    for sid in servo_ids:
        try:
            realtime_pos = servo_controller.read_angle(sid)
            if realtime_pos is False:
                continue  # 跳过无响应的舵机
            # 4. 正式锁定
            servo_controller.servo_on(sid)
            
            # 更新存储的位置信息
            current_positions[sid] = realtime_pos
            print(f"[安全锁定] 舵机{sid}已锁定, 位置: {realtime_pos}")
            
        except Exception as e:
            print(f"[安全锁定] 舵机{sid}异常: {str(e)}")
            # 标记失败舵机
            current_positions.pop(sid, None)
    
    return jsonify(
        success=True,
        locked_servos=len(servo_ids) - len(current_positions),
        failed_servos=len(servo_ids) - len(current_positions)
    )


@app.route('/bulk_unlock', methods=['POST'])
def bulk_unlock():
    for sid in current_positions:
        servo_controller.servo_off(sid)
    return jsonify(success=True)

@app.route('/save_action')
def save_action():
    action = {
        'delay': 1000,
        'servos': []
    }
    for sid, pos in current_positions.items():
        action['servos'].append({
            'id': sid,
            'position': pos,
            'mode': servo_modes[sid]
        })
    action_sequence.append(action)
    return jsonify(success=True)

@app.route('/execute_action', methods=['POST'])
def execute_action():
    data = request.json
    data = data['index']
    data = action_sequence[data]
    print(f"执行动作: {data}")
    # 设置模式
    for servo in data['servos']:
        if servo_modes.get(servo['id'], 0) != servo['mode']:
            #servo_controller.set_mode(servo['id'], servo['mode'])
            servo_modes[servo['id']] = servo['mode']
    # 执行动作
    for servo in data['servos']:
        servo_controller.set_angle(servo['id'], servo['position'], data['delay'])
    return jsonify(success=True)

@app.route('/export_sequence')
def export_sequence():
    # 生成带时间戳的文件名
    timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
    filename = f"servo_sequence_{timestamp}.json"
    
    # 创建内存文件
    file_data = {
        "metadata": {
            "export_time": timestamp,
            "total_actions": len(action_sequence),
            "controller": "LX225BusServo"
        },
        "actions": action_sequence
    }
    
    # 创建文件流
    file_stream = io.BytesIO()
    file_stream.write(json.dumps(file_data, indent=2).encode('utf-8'))
    file_stream.seek(0)
    
    return send_file(
        file_stream,
        as_attachment=True,
        download_name=filename,
        mimetype='application/json'
    )

@app.route('/import_sequence', methods=['POST'])
def import_sequence():
    global action_sequence
    
    if 'file' not in request.files:
        return jsonify(success=False, error="No file uploaded"), 400
    
    file = request.files['file']
    if file.filename == '':
        return jsonify(success=False, error="No selected file"), 400
    
    try:
        file_data = json.load(file.stream)
        # 验证文件格式
        if 'actions' not in file_data:
            return jsonify(success=False, error="Invalid file format"), 400
        print(file_data['actions'])
        action_sequence = file_data['actions']
        return jsonify(success=True, 
                      message=f"成功导入 {len(action_sequence)} 个动作序列")
        
    except json.JSONDecodeError:
        return jsonify(success=False, error="Invalid JSON file"), 400
    except Exception as e:
        return jsonify(success=False, error=str(e)), 500


@app.route('/execute_all_actions')#执行所有动作
def execute_all_actions():
    for action in action_sequence:
        for servo in action['servos']:
            servo_controller.set_angle(servo['id'], servo['position'], action['delay'])
        time.sleep(action['delay']/1000)  # 延时防止舵机指令冲突
    return jsonify(success=True)

@app.route('/delete_action', methods=['POST'])
def delete_action():
    global action_sequence
    try:
        index = request.json.get('index')
        if not isinstance(index, int) or index < 0:
            raise ValueError("Invalid index")
        
        if index >= len(action_sequence):
            return jsonify(success=False, 
                         error=f"Index {index} out of range"), 400
            
        # 安全删除（创建新列表避免内存地址突变问题）
        new_sequence = [action for i, action in enumerate(action_sequence) if i != index]
        action_sequence = new_sequence
        
        return jsonify(success=True,
                      message=f"动作序列 {index} 已删除",
                      new_count=len(action_sequence))
    
    except Exception as e:
        return jsonify(success=False,
                      error=f"删除失败: {str(e)}"), 400


@app.route('/get_servo_position')
def get_servo_position():
    data = request.json
    servo_id = data['id']
    position = servo_controller.read_angle(servo_id)
    return jsonify(position=position)


@app.route('/get_positions')
def get_positions():
    #重新获取舵机位置
    for sid in current_positions:
        current_positions[sid] = servo_controller.read_angle(sid)
    return jsonify(current_positions)

@app.route('/get_modes')
def get_modes():
    return jsonify(servo_modes)




@app.route('/get_actions')
def get_actions():
    return jsonify({
        "actions": action_sequence,
        "count": len(action_sequence)
    })

@app.route('/open_serial', methods=['POST'])
def open_serial():
    global servo_controller
    data = request.json
    port = data['port']
    baudrate = data['baudrate']
    with serial_lock:  # 使用锁来控制对串口的访问
        try:
            ser = serial.Serial(port, baudrate, timeout=0.0001)
            servo_controller = BusServo(ser)
        except serial.SerialException as e:
            return f"Error opening serial port: {e}"
        except PermissionError as e:
            return f"Permission denied: {e}"
    return "Serial port operation completed."

if __name__ == '__main__':     
    app.run(debug=True)
